﻿#region File Header
/***********************************************************************
 * Copyright © 2013 Beals Software
 * All Rights Reserved
************************************************************************
Author: Donald Beals
Date: February 4th, 2013
Description: TODO: Write a description of this file here.
****************************** Change Log ******************************
02.04.13 - Created initial file. (dbeals)
***********************************************************************/
#endregion

#region Using Statements
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
#endregion

namespace GameJolt.Async
{
	/// <summary>
	/// 
	/// </summary>
	public class AsyncResultNoResult : IAsyncResult
	{
		public enum TaskState
		{
			Pending = 0,
			CompletedSynchronously,
			CompletedAsynchronously,
			Failed,
		}

		#region Variables
		private readonly AsyncCallback asyncCallback;
		private readonly object asyncState;
		private int completedState = (int)TaskState.Pending;
		private ManualResetEvent asyncWaitHandle;
		#endregion

		#region Properties
		public object AsyncState
		{
			get
			{
				return asyncState;
			}
		}

		public bool CompletedSynchronously
		{
			get
			{
				return Thread.VolatileRead(ref completedState) == (int)TaskState.CompletedSynchronously;
			}
		}

		public WaitHandle AsyncWaitHandle
		{
			get
			{
				if(asyncWaitHandle == null)
				{
					Boolean done = IsCompleted;
					ManualResetEvent resetEvent = new ManualResetEvent(done);
					if(Interlocked.CompareExchange(ref asyncWaitHandle, resetEvent, null) != null)
						resetEvent.Close();
					else
					{
						if(!done && IsCompleted)
							asyncWaitHandle.Set();
					}
				}
				return asyncWaitHandle;
			}
		}

		public Boolean IsCompleted
		{
			get
			{
				return Thread.VolatileRead(ref completedState) != (int)TaskState.Pending;
			}
		}

		public object CoreData
		{
			get;
			private set;
		}

		public Exception Exception
		{
			get;
			set;
		}
		#endregion

		#region Constructors
		public AsyncResultNoResult(AsyncCallback asyncCallback, object asyncState, object coreData)
		{
			this.asyncCallback = asyncCallback;
			this.asyncState = asyncState;
			this.CoreData = coreData;
		}
		#endregion

		#region Methods
		public void SetAsCompleted(Exception exception, bool completedSynchronously)
		{
			this.Exception = exception;

			TaskState previousState = (TaskState)Interlocked.Exchange(ref completedState, completedSynchronously ? (int)TaskState.CompletedSynchronously : (int)TaskState.CompletedAsynchronously);
			if(previousState == TaskState.Failed)
				return;

			if(previousState != TaskState.Pending)
				throw new InvalidOperationException("You can only call SetAsCompleted once.");

			if(asyncWaitHandle != null)
				asyncWaitHandle.Set();

			try
			{
				if(asyncCallback != null)
					asyncCallback(this);
			}
			catch(Exception)
			{
				// Set the state to failed so if an exception is thrown during asyncCallback we don't cause another exception when SetAsCompleted() is called again.
				completedState = (int)TaskState.Failed;
				throw;
			}
		}

		public void EndInvoke()
		{
			if(!IsCompleted)
			{
				AsyncWaitHandle.WaitOne();
				AsyncWaitHandle.Close();
				asyncWaitHandle = null;
			}

			if(Exception != null)
				throw Exception;
		}
		#endregion
	}
}
